package cn.topcodes.tcsf.admin.commons.generic;

import cn.topcode.unicorn.utils.SpringContextHolder;
import cn.topcode.unicorn.utils.StringUtil;
import cn.topcodes.tcsf.admin.commons.domain.IdEntity;
import cn.topcodes.tcsf.admin.commons.domain.ValueRangeConstant;
import cn.topcodes.tcsf.admin.commons.generic.event.*;
import cn.topcodes.tcsf.admin.commons.query.QueryIdEntityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import tk.mybatis.mapper.entity.Example;
import javax.persistence.EntityNotFoundException;
import java.util.Date;
import java.util.List;

/**
 * 通用服务
 * @author Unicorn
 *
 * @param <T>
 */
@Transactional
public abstract class GenericService<T extends IdEntity>
		extends QueryIdEntityService<T> implements IGenericService<T> {

	@Autowired(required = false)
	private Logable logable;

	@Autowired(required = false)
	private ISessionService sessionService;

	@Override
	public T delete(Long id) {
		//	检查数据
		Assert.notNull(id, "delete(Long id) 参数id不能为null");

		T entity = this.findOne(id);
		if(entity == null) {
			throw new EntityNotFoundException("删除失败!实体不存在,ID:" + id);
		}

		//	发布删除中事件
		EntityDeletingEvent<T> entityDeletingEvent = new EntityDeletingEvent<T>();
		entityDeletingEvent.setEntity(entity);
		SpringContextHolder.publishEvent(entityDeletingEvent);

		//	执行删除操作
		this.getMapper().deleteByPrimaryKey(id);

		//	记录日志
		logGeneric(LogType.DELETE, "删除" + getName() + " " + entity);

		//	发布已删除事件
		EntityDeletedEvent entityDeletedEvent = new EntityDeletedEvent();
		entityDeletedEvent.setEntity(entity);
		SpringContextHolder.publishEvent(entityDeletedEvent);

		return entity;
	}

	/**
	 * TODO 下一步，不在接口声明此方法
	 * @param example
	 */
	@Override
	public void deleteByExample(Example example) {
		//	检查数据
		Assert.notNull(example, "deleteByExample(Example example) 参数example不能为null");

		List<T> entitys = this.findAll(example);

		//	记录日志
		logGeneric(LogType.DELETE, "条件删除" + getName() + " 条件:" + example + "，符合条件实体数:" + entitys.size());

		//	执行删除操作
		for(T entity : entitys) {
			this.delete(entity.getId());
		}
	}

	@Override
	public T delete(T t) {
		Assert.notNull(t, "delete(T t) 参数t不能为null");
		return this.delete(t.getId());
	}
	
	@Override
	public void markDelete(Long id) {
		Assert.notNull(id, "markDelete(Long id) 参数id不能为null");
		T t = this.getMapper().selectByPrimaryKey(id);
		if(t == null) {
			throw new EntityNotFoundException("标记删除失败!实体不存在,ID:" + id);
		}
		t.setDataState(ValueRangeConstant.DataState.DATA_STATE_DELETE);
		logGeneric(LogType.MARK_DELETE, "软删除" + getName() + " id:" + id);
		this.update(t);
	}
	
	@Override
	public void disable(Long id) {
		Assert.notNull(id, "disable(Long id) 参数id不能为null");
		T t = this.getMapper().selectByPrimaryKey(id);
		if(t == null) {
			throw new EntityNotFoundException("禁用失败!实体不存在,ID:" + id);
		}
		t.setDataState(ValueRangeConstant.DataState.DATA_STATE_DISABLE);
		logGeneric(LogType.DISABLE, "禁用" + getName() + " id:" + id);
		this.update(t);
	}
	
	@Override
	public void enable(Long id) {
		Assert.notNull(id, "enable(Long id) 参数id不能为null");
		T t = this.getMapper().selectByPrimaryKey(id);
		if(t == null) {
			throw new EntityNotFoundException("启用失败!实体不存在,ID:" + id);
		}
		t.setDataState(ValueRangeConstant.DataState.DATA_STATE_ENABLE);
		logGeneric(LogType.ENABLE, "启用" + getName() + " id:" + id);
		this.update(t);
	}
	
	@Override
	public void update(T t) {
		updateBy(t);
	}

	/**
	 * 更新实体
	 * @param t 实体
	 * @return	更新后的实体
	 */
	@Override
	public T updateBy(T t) {
		//	检查数据
		Assert.notNull(t, "update(T t) 参数t不能为null");
		Assert.notNull(t.getId(), "update(T t) 参数t的id不能为null");

		T entity = this.findOne(t.getId());
		//	克隆实体对象，记录更新前的状态
		T clone = (T) entity.clone();

		//	自动更新字段-更新时间，如果t没有填写状态则默认使用数据库中的值
		t.setCreatedTime(entity.getCreatedTime());
		t.setUpdatedTime(new Date());
		if(StringUtil.isBlank(t.getDataState())) {
			t.setDataState(entity.getDataState());
		}

		//	发布实体更新中事件
		EntityUpdatingEvent entityUpdatingEvent = new EntityUpdatingEvent();
		entityUpdatingEvent.setCreate(false);
		entityUpdatingEvent.setOldEntity(clone);
		entityUpdatingEvent.setNewEntity(entity);
		SpringContextHolder.publishEvent(entityUpdatingEvent);

		//	执行更新操作
		int effect = this.getMapper().updateByPrimaryKey(t);
		if(effect == 0) {
			throw new EntityNotFoundException("更新失败!实体不存在,ID:" + t.getId());
		}

		//	记录日志
		logGeneric(LogType.UPDATE, "更新" + getName() + "old:" + clone +" -> new:" + t);

		//	发布更新完成事件
		EntityUpdatedEvent entityUpdatedEvent = new EntityUpdatedEvent();
		entityUpdatedEvent.setCreate(false);
		entityUpdatedEvent.setOldEntity(clone);
		entityUpdatedEvent.setNewEntity(t);
		SpringContextHolder.publishEvent(entityUpdatedEvent);

		return t;
	}
	
	@Override
	public Long create(T t) {
		return createBy(t).getId();
	}

	/**
	 * 创建实体
	 * @param t 	实体
	 * @return	已创建的实体
	 */
	@Override
	public T createBy(T t) {
		//	检查数据
		Assert.notNull(t, "create(T t) 参数t不能为null");

		//	实体自动记录创建时间、更新时间，状态默认为启用
		Date curDate = new Date();
		t.setCreatedTime(curDate);
		t.setUpdatedTime(curDate);
		t.setDataState(ValueRangeConstant.DataState.DATA_STATE_ENABLE);

		//	发布创建中事件
		EntityCreatingEvent entityCreatingEvent = new EntityCreatingEvent();
		entityCreatingEvent.setCreate(true);
		entityCreatingEvent.setNewEntity(t);
		SpringContextHolder.publishEvent(entityCreatingEvent);

		//	执行创建操作
		int effect = this.getMapper().insert(t);
		if(effect == 0) {
			throw new IllegalStateException("创建失败!");
		}

		//	记录日志
		logGeneric(LogType.CREATE, "创建" + getName() + " " + t);

		//	发布创建完成事件
		EntityCreatedEvent entityCreatedEvent = new EntityCreatedEvent();
		entityCreatedEvent.setCreate(true);
		entityCreatedEvent.setNewEntity(t);
		SpringContextHolder.publishEvent(entityCreatedEvent);

		return t;
	}

	@Override
	public void batchDelete(Long[] ids) {
		//	检查数据
		Assert.notNull(ids, "batchDelete(Long[] ids) 参数ids不能为null");
		Assert.noNullElements(ids, "batchDelete(Long[] ids) 参数ids不能有为null元素");

		//	记录日志
		logGeneric(LogType.BATCH_DELETE, "批量删除" + getName() + " ids:" + ids);

		//	执行删除操作
		for(long id : ids) {
			this.delete(id);
		}
	}

	@Override
	public void batchDelete(T[] tArr) {
		//	检查数据
		Assert.notNull(tArr, "batchDelete(T[] tArr) 参数tArr不能为null");
		Assert.noNullElements(tArr, "batchDelete(T[] tArr) 参数tArr不能有为null元素");

		//	记录日志
		logGeneric(LogType.BATCH_DELETE, "批量删除" + getName() + " 实体列表:" + tArr);

		//	执行删除操作
		for(T t : tArr) {
			this.delete(t);
		}		
	}

	@Override
	public void batchDelete(List<T> list) {
		//	检查数据
		Assert.notNull(list, "batchDelete(List<T> list) 参数list不能为null");

		//	记录日志
		logGeneric(LogType.BATCH_DELETE, "批量删除" + getName() + " 实体列表:" + list);

		//	执行删除操作
		for(T t : list) {
			this.delete(t);
		}
	}

	@Override
	public void deleteAll() {
		//	记录日志
		logGeneric(LogType.BATCH_DELETE, "删除所有" + getName());

		Example example = this.buildExample();
		this.deleteByExample(example);
	}

	@Override
	public void batchMarkDelete(Long[] ids) {
		Assert.notNull(ids, "batchMarkDelete(Long[] ids) 参数ids不能为null");
		Assert.noNullElements(ids, "batchMarkDelete(Long[] ids) 参数ids不能有为null元素");
		logGeneric(LogType.BATCH_MARK_DELETE, "批量软删除" + getName() + " ids:" + ids);
		for(long id : ids) {
			this.markDelete(id);
		}		
	}

	@Override
	public void batchMarkDelete(List<T> list) {
		Assert.notNull(list, "batchMarkDelete(List<T> list) 参数list不能为null");
		logGeneric(LogType.BATCH_MARK_DELETE, "批量软删除" + getName() + " ids:" + list);
		for(T t : list) {
			this.markDelete(t);
		}		
	}

	@Override
	public void batchDisable(Long[] ids) {
		Assert.notNull(ids, "batchDisable(Long[] ids) 参数ids不能为null");
		Assert.noNullElements(ids, "batchDisable(Long[] ids) 参数ids不能有为null元素");
		logGeneric(LogType.BATCH_DISABLE, "批量禁用" + getName() + " ids:" + ids);
		for(long id : ids) {
			this.disable(id);
		}
	}

	@Override
	public void batchDisable(List<T> list) {
		Assert.notNull(list, "batchDisable(List<T> list) 参数list不能为null");
		logGeneric(LogType.BATCH_DISABLE, "批量禁用" + getName() + " ids:" + list);
		for(T t : list) {
			this.disable(t);
		}
	}

	@Override
	public void batchEnable(Long[] ids) {
		Assert.notNull(ids, "batchEnable(Long[] ids) 参数ids不能为null");
		Assert.noNullElements(ids, "batchEnable(Long[] ids) 参数ids不能有为null元素");
		logGeneric(LogType.BATCH_ENABLE, "批量启用" + getName() + " ids:" + ids);
		for(long id : ids) {
			this.enable(id);
		}		
	}

	@Override
	public void batchEnable(List<T> list) {
		Assert.notNull(list, "batchEnable(List<T> list) 参数list不能为null");
		logGeneric(LogType.BATCH_ENABLE, "批量启用" + getName() + " list:" + list);
		for(T t : list) {
			this.enable(t);
		}		
	}

	@Override
	public void batchUpdate(List<T> list) {
		Assert.notNull(list, "batchUpdate(List<T> list) 参数list不能为null");
		logGeneric(LogType.BATCH_UPDATE, "批量更新" + getName() + " list:" + list);
		for(T t : list) {
			this.update(t);
		}
	}

	@Override
	public void batchCreate(List<T> list) {
		Assert.notNull(list, "batchCreate(List<T> list) 参数list不能为null");
		logGeneric(LogType.BATCH_CREATE, "批量创建" + getName() + " list:" + list);
		for(T t : list) {
			this.create(t);
		}		
	}
	
	@Override
	public void batchCreate(T[] tArr) {
		Assert.notNull(tArr, "batchCreate(T[] tArr) 参数tArr不能为null");
		Assert.noNullElements(tArr, "batchCreate(T[] tArr) 参数tArr不能有为null元素");
		logGeneric(LogType.BATCH_CREATE, "批量创建" + getName() + " list:" + tArr);
		for(T t : tArr) {
			this.create(t);
		}
	}

	@Override
	public void markDelete(T t) {
		Assert.notNull(t, "markDelete(T t) 参数t不能为null");
		this.markDelete(t.getId());
	}

	@Override
	public void disable(T t) {
		Assert.notNull(t, "disable(T t) 参数t不能为null");
		this.disable(t.getId());
	}

	@Override
	public void enable(T t) {
		Assert.notNull(t, "enable(T t) 参数t不能为null");
		this.enable(t.getId());
	}

	@Override
	public void batchUpdate(T[] tArr) {
		Assert.notNull(tArr, "batchUpdate(T[] tArr) 参数tArr不能为null");
		Assert.noNullElements(tArr, "batchUpdate(T[] tArr) 参数tArr不能有为null元素");
		logGeneric(LogType.BATCH_UPDATE, "批量更新" + getName() + " list:" + tArr);
		for(T t : tArr) {
			this.update(t);
		}
	}

	@Override
	public void save(T t) {
		Assert.notNull(t, "save(T t) 参数t不能为null");
		if(t.getId() == null) {
			this.create(t);
		}else {
			this.update(t);
		}
	}

	@Override
	public void batchSave(List<T> ts) {
		Assert.notNull(ts, "batchSave(T[] ts) 参数ts不能为null");
		for(T t : ts) {
			this.save(t);
		}
	}

	/*OperateLogConfig*/
	private String name;

	private Boolean genericLog = null;

	protected boolean isGenericLog() {
		if(genericLog != null) return genericLog;
		OperateLogConfig desc = (OperateLogConfig) this.getEntityClass().getAnnotation(OperateLogConfig.class);
		return genericLog = desc != null ? desc.generic() : true;
	}

	protected String getName() {
		if(name != null) return name;
		OperateLogConfig desc = (OperateLogConfig) this.getEntityClass().getAnnotation(OperateLogConfig.class);
		return name = desc != null && StringUtil.isNotBlank(desc.name()) ? desc.name() : this.getEntityClass().getName();
	}

	protected void logGeneric(String type, String content) {
		if(isGenericLog() && logable != null &&
				sessionService != null && sessionService.getCurrentUserId() != null) {
			logable.log(sessionService.getCurrentUserId(), type, content);
		}
	}
}
